﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Data;
using SharpDiary.Core.Common;
using System.Windows.Forms;
using SharpDiary.Core.Attributes;
using System.IO;

namespace SharpDiary.Core.PlugIn
{
    /// <summary>
    /// 插件管理者。
    /// </summary>
    public static class PlugInManager
    {
        #region Fields & Properties

        private static readonly string PLUGINS_FOLDER_NAME = "PlugIns"; //存放插件的文件夹名称
        private static DataTable _assemblyInfoTable = new DataTable();  //用于存储已加载的 Assembly 相关信息。

        #endregion

        #region Constructors

        static PlugInManager()
        {
            BuildAssemblyInfoTable();
        }

        #endregion

        #region Methods

        /// <summary>
        /// 创建用于暂存已加载的 Assembly 实例的 DataTable 结构。
        /// </summary>
        private static void BuildAssemblyInfoTable()
        {
            _assemblyInfoTable.Columns.Add(new DataColumn("id", typeof(string)));
            _assemblyInfoTable.Columns.Add(new DataColumn("assemblyString", typeof(string)));
            _assemblyInfoTable.Columns.Add(new DataColumn("assembly", typeof(Assembly)));
            _assemblyInfoTable.Columns.Add(new DataColumn("instance", typeof(object)));
            _assemblyInfoTable.Columns.Add(new DataColumn("createdDatetime", typeof(DateTime)));
            _assemblyInfoTable.Columns.Add(new DataColumn("AppDomainInstance", typeof(object)));
        }

        /// <summary>
        /// 卸载所有已加载的插件。
        /// </summary>
        public static void Clear()
        {
            foreach (DataRow row in _assemblyInfoTable.Rows)
            {
                AppDomain doMain = row["AppDomainInstance"] as AppDomain;
                object instance = row["instance"];

                //销毁所有缓存的对象引用
                if (instance != null) { instance = null; }

       
                if (doMain != null)
                {
                    AppDomain.Unload(doMain);
                }
         
            }
        }

        /// <summary>
        /// 获取指定 Assembly 的默认实例。
        /// </summary>
        /// <param name="assemblyString"></param>
        /// <param name="fullTypeName">要实例化的类全名称，包括完整的命名空间</param>
        /// <param name="moduleId">插件 guid 字符串编码</param>
        /// <returns></returns>
        public static object GetModuleInstance(string assemblyString, string moduleId, string fullTypeName)
        {
            if (string.IsNullOrEmpty(assemblyString) || string.IsNullOrEmpty(moduleId) || string.IsNullOrEmpty(fullTypeName)) { return null; }

            DataRow[] rows = _assemblyInfoTable.Select(string.Format("assemblyString = '{0}' and id = '{1}'", assemblyString, moduleId), "createdDatetime DESC");
            if (rows.Length > 0)
            {
                object instance = rows[0]["instance"];
                if (instance != null)
                {
                    if (instance is System.Windows.Forms.Form)
                    {
                        if (!(instance as System.Windows.Forms.Form).IsDisposed)
                        {
                            return instance;
                        }
                        else //清除 _assemblyInfoTable 中已被销毁的窗体实例对应的记录行
                        {
                            rows[0].Delete();
                            _assemblyInfoTable.AcceptChanges();
                        }
                    }
                    else
                    {
                        return instance;
                    }
                }
                else//清除 _assemblyInfoTable 中已被销毁的窗体实例对应的记录行
                {
                    rows[0].Delete();
                    _assemblyInfoTable.AcceptChanges();
                }
            }

            AppDomainSetup setup = new AppDomainSetup();
            string plugInPath = Path.Combine(Application.StartupPath, PLUGINS_FOLDER_NAME);
            setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
            setup.PrivateBinPath = plugInPath;
            setup.CachePath = Path.Combine(plugInPath, @"cache\");
            setup.ShadowCopyFiles = "true";
            setup.ShadowCopyDirectories = plugInPath;

            AppDomain newAppDomain = AppDomain.CreateDomain(moduleId, AppDomain.CurrentDomain.Evidence, setup);

            newAppDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
            IPlugIn plugInInstance = newAppDomain.CreateInstanceAndUnwrap(assemblyString, fullTypeName) as IPlugIn;

            _assemblyInfoTable.Rows.Add(moduleId, assemblyString, null, plugInInstance, DateTime.Now, newAppDomain); //登记已加载的 PlugIn 实例的相关信息。

            return plugInInstance;
        }

        static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            return Assembly.LoadFrom(args.Name);
        }

        #endregion
    }
}
